#include "../../core/graphic/texture.h"
#include <d3dx9.h>
extern LPDIRECT3DDEVICE9       g_pd3dDevice;


struct D3D_TEXTURE_DESC
{
	int w;
	int h;
	KFormat format;
	LPDIRECT3DTEXTURE9 pTexture;
	D3DLOCKED_RECT lockedRect;
	BYTE* dataTemp;
	D3D_TEXTURE_DESC() {pTexture = NULL; lockedRect.pBits = NULL; dataTemp = NULL;}
};


KTexture::KTexture()
{
	mTextureData = new D3D_TEXTURE_DESC;
	mBpp = 0;
}

KTexture::~KTexture()
{
	Reset();
	D3D_TEXTURE_DESC* pTex = (D3D_TEXTURE_DESC*)mTextureData;
	delete pTex;
}

void KTexture::Reset()
{
	D3D_TEXTURE_DESC* pTex = (D3D_TEXTURE_DESC*)mTextureData;
	if (pTex->pTexture)
		pTex->pTexture->Release();
	pTex->pTexture = NULL;
	pTex->lockedRect.pBits = NULL;
}

// Implementation dependent methods
bool KTexture::CreateFromFile(const char* fileName, bool forceGray)
{
	Reset();

	KBitmap::CreationDesc cdesc;
	cdesc.forceGray = forceGray;
	return KBitmap::LoadFromFile(fileName, this, cdesc);
}

bool KTexture::CreateFromScratch(int w, int h, KFormat format)
{
	D3D_TEXTURE_DESC* pTex = (D3D_TEXTURE_DESC*)mTextureData;
	return Allocate(w, h, format);
}

bool KTexture::UpdateRect(int sx, int sy, int w, int h, void* pData)
{
	D3D_TEXTURE_DESC* pTex = (D3D_TEXTURE_DESC*)mTextureData;
	if (sx + w > pTex->w ||
		sy + h > pTex->h)
		return false;
	D3DLOCKED_RECT lockedRect;
	RECT rect;
	rect.left = sx;
	rect.right = sx + w;
	rect.top = sy;
	rect.bottom = sy + h;
	HRESULT hr = pTex->pTexture->LockRect(0, &lockedRect, &rect, 0);
	if (FAILED(hr)) 
		return false;

	BYTE* pDestData = (BYTE*)lockedRect.pBits;

	if (pTex->format == eRGBA_8888) {

		for (int y = 0; y < h; ++y) {
			BYTE* pSrcData = (BYTE*)pData + y * w * 4;

			for (int x = 0; x < w; ++x) {
				pDestData[x*4 + 0] = pSrcData[x*4 + 2];
				pDestData[x*4 + 1] = pSrcData[x*4 + 1];
				pDestData[x*4 + 2] = pSrcData[x*4 + 0];
				pDestData[x*4 + 3] = pSrcData[x*4 + 3];
			}
			pDestData += lockedRect.Pitch;
		}
	}
	else if (pTex->format == eRGBA_L8) {

		for (int y = 0; y < h; ++y) {
			BYTE* pSrcData = (BYTE*)pData + y * w;

			for (int x = 0; x < w; ++x) {
				pDestData[x] = pSrcData[x];
			}

			pDestData += lockedRect.Pitch;
		}

	}
	

	pTex->pTexture->UnlockRect(0);
	return true;
}


bool KTexture::Bind()
{
	D3D_TEXTURE_DESC* pTex = (D3D_TEXTURE_DESC*)mTextureData;
	g_pd3dDevice->SetTexture( 0, pTex->pTexture );
	if (pTex->format == eRGBA_8888)
		g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
	else
		g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );
	return true;
}

bool KTexture::BeginEdit()
{
	D3D_TEXTURE_DESC* pTex = (D3D_TEXTURE_DESC*)mTextureData;
	if (pTex->format == eRGBA_L8) {
		pTex->dataTemp = new BYTE[pTex->w * pTex->h];
		return true;
	}
	else if (pTex->format == eRGBA_8888) {
		HRESULT hr = pTex->pTexture->LockRect(0, &pTex->lockedRect, NULL, 0);
		if (FAILED(hr)) {
			pTex->lockedRect.pBits = NULL;
			return false;
		}
		else
			return true;	
	}
	else
		return false;
}

void KTexture::EndEdit()
{
	D3D_TEXTURE_DESC* pTex = (D3D_TEXTURE_DESC*)mTextureData;
	if (pTex->format == eRGBA_L8) {
		UpdateRect(0, 0, pTex->w, pTex->h, pTex->dataTemp);
		delete[] pTex->dataTemp;
		pTex->dataTemp = NULL;
	}
	else if (pTex->format == eRGBA_8888) {
		for (int y = 0; y < pTex->h; ++y) {
			BYTE* pLine = (BYTE*)pTex->lockedRect.pBits + pTex->lockedRect.Pitch * y;
			for (int x = 0; x < pTex->lockedRect.Pitch; x += 4)
				std::swap(pLine[x], pLine[x+2]);
		}
		pTex->pTexture->UnlockRect(0);
		pTex->lockedRect.pBits = NULL;
	}
}

bool KTexture::Allocate(int w, int h, KFormat format)
{
	D3D_TEXTURE_DESC* pTex = (D3D_TEXTURE_DESC*)mTextureData;
	mWidth = w;
	mHeight = h;
	pTex->w = w;
	pTex->h = h;
	pTex->format = format;

	D3DFORMAT d3dFormat = D3DFMT_UNKNOWN;
	if (format == eRGBA_8888) {
		d3dFormat = D3DFMT_A8R8G8B8;
		mBpp = 4;
	}
	else if (format == eRGBA_L8) {
		d3dFormat = D3DFMT_A8;
		mBpp = 1;
	}
	else {
		// TODO: support more formats.
		mBpp = 0;
	}

	if (!g_pd3dDevice)
		return false;

	HRESULT hr = g_pd3dDevice->CreateTexture((UINT)w, (UINT)h,
		1, // level count
		0,
		d3dFormat,
		D3DPOOL_MANAGED,
		&pTex->pTexture,
		NULL);

	return SUCCEEDED(hr);
}

void* KTexture::GetRowPtr(int row)
{
	D3D_TEXTURE_DESC* pTex = (D3D_TEXTURE_DESC*)mTextureData;

	if (pTex->format == eRGBA_8888) {
		if (pTex->lockedRect.pBits) {
			BYTE* pDataInByte = (BYTE*)pTex->lockedRect.pBits;
			return &pDataInByte[row * pTex->lockedRect.Pitch];
		}
		else
			return NULL;
	}
	else if (pTex->format == eRGBA_L8) {
		if (pTex->dataTemp)
			return &pTex->dataTemp[pTex->w * row];
		else
			return NULL;
	}
	else
		return NULL;
}
